/*
    bookings = {
        key: {
            price:
            cancelFlag:
            day:
        }
    }
 key: userid&&date&&time&&place
 */
 var log = console.log.bind(console);
 log = function() {};

function Booker(configs) {

    this.prices = configs.prices;
    this.punishs = configs.punishs;
    this.map = configs.map;
    this.format = configs.format;
    this.patterns = configs.patterns;
    this.places = configs.places;

    this.bookings = {};
} 

//judge line logic
Booker.prototype.judgeLine = function(line) {
    var data = line.split(" ");
    var len = data.length;

    if(len === 4) {
        this.data = data;
        return "booking";
    } else if(len === 5) {
        this.data = data;
        return "cancel";
    } else if(len === 1 && data[0] === "") {
        this.data = data;
        return "printFee";
    } else { 
        this.data = data;
        return "booking";
    }
};

//addBooking logic
Booker.prototype.isValidBooking = function (userID, date, timeRange, place) {
    //check if the date is valid.

    log("userID, date, timeRange, place", userID, date, timeRange, place);
    var dateValid = isValidDate(date, this.patterns);
    var timeValid = isValidTime(timeRange, this.patterns);
    var userIdValid = isValidUser(userID, this.patterns);
    var placeValid = isValidPlace(place, this.patterns);

    if(dateValid && timeValid && userIdValid && placeValid) {
        return true;
    } else {
        log("date,time, user, place", dateValid, timeValid, userIdValid, placeValid);
        return false;
    }
};

Booker.prototype.isConflict = function(date, timeRange, place) {
    for(var key in this.bookings) {

        if(this.bookings[key].cancelFlag === true) {
            continue;
        }

        var records = key.split(this.format.keyJoin);

        if(place !== records[this.format.place]) {
            continue;
        } else if(date !== records[this.format.date]) {
            continue;
        } else {
            var start = timeRange.split(this.format.timeJoin)[0];
            var end = timeRange.split(this.format.timeJoin)[1];

            var timeRangeRecord = key.split(this.format.keyJoin)[this.format.timeRange];
            var startRecord = timeRangeRecord[0];
            var endRecord = timeRangeRecord[1];

            if(end <= startRecord || start >= endRecord) {
                continue;
            }else {
                return true;
            }
        }
    }
    return false;
};

Booker.prototype.calcDay = function(date) {
    var date_arr = this.patterns.datePattern.exec(date);
    var d = new Date(parseInt(date_arr[1]), parseInt(date_arr[2])-1, parseInt(date_arr[3]));
    return d.getDay();
}

Booker.prototype.calcPrice = function(day, timeRange) {
    var sum = 0;

    var time_arr = this.patterns.timePattern.exec(timeRange);
    var start = parseInt(time_arr[1]);
    var end = parseInt(time_arr[2]);

    var kind = this.map[day];
    var prices = this.prices[kind];
    for (var i = 0; i < prices.length; i++) {
        var left = prices[i].start;
        var right = prices[i].end;

        log("left, right, start, end", left, right, start, end);

        if(start >=left && end <= right) {
            sum += (end - start) * prices[i].price;
            break;
        }else if(start <= left && end < right && end > left) {
            sum += (end - left) * prices[i].price;
        }else if(start > left && start < right && end >= right) {
            sum += (right - start) * prices[i].price;
        }else if(start >= right || end <= left) {
            sum += 0;
        }else {
            sum += (right - left) * prices[i].price;
        }
    }

    log(timeRange + "sum: ", sum);

    return sum;
};

Booker.prototype.addBooking = function() {
    if(!this.data) return;

    log("data", this.data);

    var date = this.data[this.format.date];
    var timeRange = this.data[this.format.timeRange];
    var place = this.data[this.format.place];
    var userID = this.data[this.format.userID];

    if(!this.isValidBooking(userID, date, timeRange, place)) {
        console.log("> Error: the booking is invalid!");
    } else if(this.isConflict(date, timeRange, place)) {
        console.log("> Error: the booking conflicts with existing bookings!");
    } else {
        var day = this.calcDay(date);
        var price = this.calcPrice(day, timeRange);
        
        var key = this.data.join(this.format.keyJoin);

        this.bookings[key] = {};
        this.bookings[key].price = price;
        this.bookings[key].day = day;
        this.bookings[key].cancelFlag = false;

        log(this.bookings);

        console.log("> Success: the booking is accepted!");
    }   
};

//cancel logic
Booker.prototype.cancel = function() {
    var cancelFlag = this.data[this.data.length - 1];
    if(cancelFlag != "C") {
        console.log("> Error: the cancelling is invalid!"); //invalid cancel flag
        return;
    } else {
        var keys_arr = this.data.slice(0,this.data.length - 1);
        var key = keys_arr.join(this.format.keyJoin);

        for(var index in this.bookings) {
            if(index === key && this.bookings[index].cancelFlag === false) {
                this.bookings[index].cancelFlag = true;
                var kind = this.map[this.bookings[index].day];
                var punish = this.punishs[kind];
                var new_price = parseInt(this.bookings[index].price) * punish;
                this.bookings[index].price = new_price;
                console.log("> Success: the cancelling is accepted!");
                return;
            }
        }
        console.log("> Error: the booking being cancelled does not exist!");
    }
}

Booker.prototype.printFee = function() {
    //classify the data in this.bookings.
    log(this.places);
    var result = classifyData(this.bookings, this.format, this.places);
    
    //print the result
    printResult(result, this.format); 
}

Booker.prototype.receive = function(line) {
    var flag = this.judgeLine(line);

    switch(flag) {
        case "booking": 
            this.addBooking();
            break;
        
        case "cancel":
            this.cancel();
            break;
       
        case "printFee":
            this.printFee();
            break;
        default:
            return;
    }
};

//tool functions.
function isValidDate(yyyyMMdd, patterns) {
    // yyyy: 1000-2999
    // MM: 01-12
    // dd: 01-31
    var reg = patterns.datePattern;

    var arr = reg.exec(yyyyMMdd);

    if(arr === null) {
        return false;
    }

    if(arr[0] === yyyyMMdd) {
        var d = new Date(parseInt(arr[1]), parseInt(arr[2])-1, parseInt(arr[3]));

        /*if time is before now, can't do the booking.(but the example is not proposed to check this item)
        if(d.getMilliseceonds() < Date.now()) return false;
        */

        //to handle the leap month.
        return (d.getMonth()+1)==parseInt(arr[2]) && d.getDate()==parseInt(arr[3]);
    } else {
        return false;
    }
}

function isValidTime(timeRange, patterns) {
    var pattern = patterns.timePattern;

    if(!pattern.test(timeRange)) {
        return false;
    } else {
        var time_arr = pattern.exec(timeRange);
        var start = parseInt(time_arr[1]);
        var end = parseInt(time_arr[2]);

        if(start < 9 || end > 22) { //to ensure the badminton court is open
            return false;
        } else if((start - end) >= 0) { //to ensure the time range is rational
            return false;
        } else {
            return true;
        }
    }
}

function isValidUser(userID, patterns) {
    var pattern = patterns.userPattern;
    if(pattern.test(userID)) {
        return true;
    } else {
        return false;
    }
}

function isValidPlace(place, patterns) {
    var pattern = patterns.placePattern;
    return pattern.test(place);
}

function classifyData(bookings, format, places) {
    log("in classifyData");

    log(places);
    var result = {};
    for(var i = 0; i < places.length; i++) {
        result[places[i]] = {};
        result[places[i]].data = [];
        result[places[i]].total = 0;
    }

    for(var key in bookings){
        var key_arr = key.split(format.keyJoin);

        var len = key_arr.length;
        var key_now = key_arr.slice(1, len - 1).join(format.keyJoin);
        
        var place = key_arr[format.place];
       
        var temp = {};
        temp.key = key_now;
        temp.price = bookings[key].price;
        temp.flag = bookings[key].cancelFlag;
        result[place].data.push(temp);

        result[place].total += bookings[key].price;
    }

    for(var index in result) {
        result[index].data.sort(function(a,b) {
            if(a.key < b.key) {
                return -1;
            }else if(a.key > b.key) {
                return 1;
            }else {
                return 0;
            }
        });
    }

    log(result);
    return result;
}

function printResult(result, format) {

    var output = [];
    var header = "> 收入汇总\n> ---"
    var sum = 0;

    log("result in printResult", result);
    for(var place in result) {
        var str = "> 场地:" + place + "\n";
        for(var i = 0; i < result[place].data.length; i++) {
            var key = result[place].data[i].key;
            var keyCopy = key;
            log("key in print: ", key);

            if(result[place].data[i].flag === true) {
                str += "> ";
                str += keyCopy.replace(format.keyJoin, " ");
                str += " 违约金 ";
                str = str + result[place].data[i].price + "元\n";
            }else {
                str += "> ";
                str += keyCopy.replace(format.keyJoin, " ");
                str = str + " " + result[place].data[i].price + "元\n";
            } 
        }
        
        str = str + "> 小计: " + result[place].total + "元";
        output.push(str);
        sum += result[place].total;
    }

    var footer = "> ---\n> 总计: " + sum + "元";
    console.log(header);
    console.log(output.join("\n>\n"));
    console.log(footer);
}

exports.Booker = Booker;